package com.softserveinc.data;

import java.text.ParseException;
import java.util.ArrayList;
import java.util.List;

import org.json.JSONException;

import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.os.AsyncTask;
import android.text.TextUtils;
import android.util.Log;

import com.softserveinc.R;
import com.softserveinc.data.cache.CacheManager;
import com.softserveinc.data.cache.ICacheManager;
import com.softserveinc.data.help.MovieListType;
import com.softserveinc.fragments.utils.Image;
import com.softserveinc.model.Movie;
import com.softserveinc.model.MovieDetails;
import com.softserveinc.model.MovieDetailsData;
import com.softserveinc.model.MovieListData;
import com.softserveinc.network.ApiClient;
import com.softserveinc.network.BitmapManager;

public class MovieDataManager {
	private static final String TAG = MovieDataManager.class.getSimpleName();

	private final List<Movie> moviesByRating = new ArrayList<Movie>();
	private final List<Movie> moviesByReleaseData = new ArrayList<Movie>();
	private final List<Movie> moviesByTitle = new ArrayList<Movie>();
	private final List<Movie> searchedMovies = new ArrayList<Movie>();

	private final ApiClient apiClient;
	private final ICacheManager cacheManager;

	private final BitmapManager bitmapManager;

	private final Context context;

	private String searchQuery;

	private ProgressCallBack progressCallBack;

	private GetMovieListImagesTask listImagesTask;
	private GetMovieDetailsTask detailsTask;
	private GetPosterTaskTask detailsThumbsTask;

	public static final int PAGE_SIZE = 20;
	public static final int MAX_MOVIE_LIST_SIZE = 60;

	private GetPageableMovieListTask pageableListTask;

	public MovieDataManager(final Context context) {
		this.context = context;

		apiClient = new ApiClient(context);
		cacheManager = new CacheManager(context);
		bitmapManager = new BitmapManager(context);
	}

	public void manageDataForMovieList(final MovieListCallBack callback,
			final MovieListType type) {
		Log.w(TAG, "manageDataForMovieList" + type.name());
		new GetMovieListTask(callback).execute(type);
	}

	public void manageDataForMovieList(
			final PageableMovieListCallBack callback, final MovieListType type,
			final int page) {
		if (pageableListTask != null
				&& !pageableListTask.getStatus().equals(
						AsyncTask.Status.FINISHED)) {
			return;
		}

		pageableListTask = new GetPageableMovieListTask(callback);
		pageableListTask.execute(new PagingWrapper(type, page));
	}

	public void manageDataForMovieList(final String searchQuery,
			final MovieListCallBack callback) {
		new GetMovieSearchListTask(callback).execute(searchQuery);
	}

	public void manageDataForMovieListImages(final List<Movie> movies,
			final MoviePosterCallBack callback) {
		listImagesTask = new GetMovieListImagesTask(callback);

		listImagesTask.execute(new ListWrapper(movies));
	}

	public void recycleImages(final MovieListType movieListType) {
		List<Movie> movies = getDemandedListFromRuntime(movieListType);

		for (Movie movie : movies) {
			Bitmap poster = movie.getPoster();

			if (poster != null && !poster.isRecycled()) {
				poster.recycle();
			}
		}
	}

	public void interruptLoadingPosters(final MoviePosterCallBack callback) {
		if (listImagesTask != null) {
			listImagesTask.cancel(true);
		}
	}

	public void manageDataForMovieDetails(final Movie movie,
			final MovieDetailsCallBack movieDetailsCallBack) {
		if (detailsTask != null) {
			detailsTask.cancel(true);
		}

		detailsTask = new GetMovieDetailsTask(movieDetailsCallBack);

		detailsTask.execute(movie);
	}

	public void interruptLoadingMovieDetails(
			final MovieDetailsCallBack movieDetailsCallBack) {
		if (detailsTask != null) {
			detailsTask.cancel(true);
		}
	}

	public void loadPosterOnDetailsScreen(final Movie movie,
			final MovieDetailsCallBack movieListCallBack) {
		if (detailsThumbsTask != null) {
			detailsThumbsTask.cancel(true);
		}

		detailsThumbsTask = new GetPosterTaskTask(movieListCallBack);
		detailsThumbsTask.execute(movie);
	}

	public void interruptLoadingThumbsOnDetails(
			final MovieDetailsCallBack movieListCallBack) {
		if (detailsThumbsTask != null) {
			detailsThumbsTask.cancel(true);
		}
	}

	public void manageDataForMovieDetailsImages(
			final MovieDetails movieDetails,
			final MovieDetailsCallBack movieDetailsCallBack) {
		new GetMovieDetailsImagesTask(movieDetailsCallBack)
				.execute(movieDetails);
	}

	private List<Movie> getMovieList(final String query) throws JSONException,
			ParseException {
		if (TextUtils.isEmpty(searchQuery)
				|| !searchQuery.equalsIgnoreCase(query)) {

			searchQuery = query;

			List<Movie> movies = cacheManager.getListBySearchQuery(query);

			if (movies == null || movies.isEmpty()) {
				movies = apiClient.getMovieList(query);

				cacheManager.insertSearchedMovieList(query, movies);
			}

			searchedMovies.clear();
			searchedMovies.addAll(movies);
		}

		return searchedMovies;
	}

	public List<Movie> getMovieList(final MovieListType order)
			throws JSONException, ParseException {
		List<Movie> moviesFromRuntime = getDemandedListFromRuntime(order);

		if (moviesFromRuntime.isEmpty()) {

			List<Movie> movies = cacheManager.getListByOrder(order);

			if (movies == null || movies.isEmpty()) {
				movies = apiClient.getMovieList(order.getOrder(), ""
						+ PAGE_SIZE);

				cacheManager.insertMovieList(movies, order);
			}

			moviesFromRuntime.clear();
			moviesFromRuntime.addAll(movies);
		}

		return moviesFromRuntime;
	}

	public List<Movie> getMovieList(final MovieListType order, final int page)
			throws JSONException, ParseException {
		List<Movie> moviesFromRuntime = getDemandedListFromRuntime(order);

		List<Movie> movies = apiClient.getMovieList(order.getOrder(), ""
				+ PAGE_SIZE, page);

		cacheManager.addMovieList(movies, order);

		List<Movie> result = new ArrayList<Movie>();

		for (Movie movie : movies) {
			if (!moviesFromRuntime.contains(movie)) {
				result.add(movie);
			}
		}

		moviesFromRuntime.addAll(result);

		Log.e(TAG, "list size " + moviesFromRuntime.size());

		return result;
	}

	private List<Movie> getDemandedListFromRuntime(final MovieListType order) {
		switch (order) {
		case RATING:
			return moviesByRating;

		case RELEASE_DATE:
			return moviesByReleaseData;

		case TITLE:
			return moviesByTitle;
		}
		return null;
	}

	public void setPosterToMovie(final Movie movie) {
		Bitmap bitmap;
		try {
			bitmap = bitmapManager.getBitmapForLocalUrl(movie
					.getPosterLocalUrl());
		} catch (Exception e) {
			bitmap = bitmapManager
					.getBitmapForHttpUrl(movie.getPosterHttpUrl());
			if (bitmap != null) {
				movie.setPosterLocalUrl(bitmapManager.saveImage(bitmap));
				cacheManager.insertOrUpdate(movie);
			} else {
				bitmap = BitmapFactory.decodeResource(context.getResources(),
						R.drawable.no_image);
			}
		}

		movie.setPoster(bitmap);
	}

	private MovieDetails getMovieDetails(final Movie movie)
			throws JSONException, ParseException {
		MovieDetails movieDetails = cacheManager.getMovieDetails(movie);

		if (movieDetails == null) {
			movieDetails = apiClient.getMovieDetails(movie.getId());

			movieDetails.setMovie(movie);

			cacheManager.insertOrUpdate(movieDetails);
		}

		return movieDetails;
	}

	private class Pair {
		private int index;
		private Image image;
	}

	private Pair getMovieDetailsImage(final MovieDetails details, final int i) {
		Image image = new Image();
		try {
			String fileName = details.getThumbLocalUrls().get(i);

			Bitmap bitmap = bitmapManager.getBitmapForLocalUrl(fileName);

			image = new Image(bitmap, fileName);
		} catch (Exception e) {
			Log.e(TAG, "error" + e.getMessage(), e);

			Bitmap bitmap = bitmapManager.getBitmapForHttpUrl(details
					.getThumbHttpUrls().get(i));

			if (bitmap != null) {
				String fileName = bitmapManager.saveImage(bitmap);

				image = new Image(bitmap, fileName);

				cacheManager.updateThumbImage(details.getMovie(), details
						.getThumbHttpUrls().get(i), details
						.getThumbHttpUrlsBig().get(i), fileName);
			}
		}

		image.setPosterUrl(details.getThumbHttpUrlsBig().get(i));

		Pair pair = new Pair();
		pair.index = i;
		pair.image = image;
		return pair;
	}

	public interface MovieListCallBack {
		void postExecute(final MovieListData movies);
	}

	public interface PageableMovieListCallBack {
		void postExecuteLoading(final MovieListData movies);

		void restrictLoading();

		void allowDownloading();
	}

	public interface MoviePosterCallBack {
		void posterLoaded();
	}

	public interface MovieDetailsCallBack {
		void postExecute(final MovieDetailsData movieDetails);

		void loadImage(final Image image, final int index);

		void loadPoster(final Movie movie);
	}

	public interface ProgressCallBack {

		void showSpinner();

		void hideSpinner();

		void showAlertDialog(final String message);
	}

	private class GetMovieListTask extends
			AsyncTask<MovieListType, Void, MovieListData> {

		private final MovieListCallBack movieListCallBack;

		public GetMovieListTask(final MovieListCallBack movieListCallBack) {
			this.movieListCallBack = movieListCallBack;
		}

		@Override
		protected void onPreExecute() {
			super.onPreExecute();

			progressCallBack.showSpinner();
		}

		@Override
		protected MovieListData doInBackground(final MovieListType... params) {
			MovieListData data = new MovieListData();

			try {
				if (params == null || params.length == 0) {
					throw new IllegalArgumentException(TAG);
				} else {
					data.setMovies(getMovieList(params[0]));
				}
			} catch (Exception e) {
				String err = (e.getMessage() == null) ? "SD Card failed" : e
						.getMessage();
				Log.e(TAG, err, e);

				data.setMovies(null);
				data.setException(e);
			}

			return data;
		}

		@Override
		protected void onPostExecute(final MovieListData movieData) {
			super.onPostExecute(movieData);
			if (movieData.getException() == null) {
				movieListCallBack.postExecute(movieData);
			} else {
				progressCallBack.showAlertDialog("Warning:"
						+ movieData.getException().getMessage());
			}
			progressCallBack.hideSpinner();
		}
	}

	private class GetPageableMovieListTask extends
			AsyncTask<PagingWrapper, Void, MovieListData> {

		private final PageableMovieListCallBack movieListCallBack;

		public GetPageableMovieListTask(
				final PageableMovieListCallBack movieListCallBack) {
			this.movieListCallBack = movieListCallBack;
		}

		@Override
		protected void onPreExecute() {
			super.onPreExecute();

			progressCallBack.showSpinner();
		}

		@Override
		protected MovieListData doInBackground(final PagingWrapper... params) {
			MovieListData data = new MovieListData();

			try {
				if (params == null || params.length == 0) {
					throw new IllegalArgumentException(TAG);
				} else {
					PagingWrapper wrapper = params[0];

					data.setMovies(getMovieList(wrapper.type, wrapper.page));
				}
			} catch (Exception e) {
				String err = (e.getMessage() == null) ? "SD Card failed" : e
						.getMessage();
				Log.e(TAG, err, e);

				data.setMovies(null);
				data.setException(e);
			}

			return data;
		}

		@Override
		protected void onPostExecute(final MovieListData movieData) {
			super.onPostExecute(movieData);
			if (movieData.getException() == null) {
				movieListCallBack.postExecuteLoading(movieData);
			} else {
				progressCallBack.showAlertDialog("Warning:"
						+ movieData.getException().getMessage());
			}
			progressCallBack.hideSpinner();
		}
	}

	private class PagingWrapper {
		private MovieListType type;
		private int page;

		public PagingWrapper(final MovieListType type, final int page) {
			super();
			this.type = type;
			this.page = page;
		}
	}

	private class GetMovieSearchListTask extends
			AsyncTask<String, Void, MovieListData> {

		private final MovieListCallBack movieListCallBack;

		public GetMovieSearchListTask(final MovieListCallBack movieListCallBack) {
			this.movieListCallBack = movieListCallBack;
		}

		@Override
		protected void onPreExecute() {
			super.onPreExecute();

			progressCallBack.showSpinner();
		}

		@Override
		protected MovieListData doInBackground(final String... params) {
			MovieListData data = new MovieListData();

			try {
				if (params == null || params.length == 0) {
					throw new IllegalArgumentException(TAG);
				} else {
					data.setMovies(getMovieList(params[0]));
				}
			} catch (Exception e) {
				String err = (e.getMessage() == null) ? "SD Card failed" : e
						.getMessage();
				Log.e(TAG, err, e);

				data.setMovies(null);
				data.setException(e);
			}

			return data;
		}

		@Override
		protected void onPostExecute(final MovieListData movieData) {
			super.onPostExecute(movieData);
			if (movieData.getException() == null) {
				movieListCallBack.postExecute(movieData);
			} else {
				progressCallBack.showAlertDialog("Err:"
						+ movieData.getException().getMessage());
			}
			progressCallBack.hideSpinner();
		}
	}

	private class ListWrapper {
		private List<Movie> movies;

		public ListWrapper(final List<Movie> movies) {
			this.movies = movies;
		}
	}

	private class GetMovieListImagesTask extends
			AsyncTask<ListWrapper, Movie, Integer> {

		private final MoviePosterCallBack moviePosterCallBack;

		public GetMovieListImagesTask(
				final MoviePosterCallBack moviePosterCallBack) {
			this.moviePosterCallBack = moviePosterCallBack;
		}

		@Override
		protected void onPreExecute() {
			super.onPreExecute();

			progressCallBack.showSpinner();
		}

		@Override
		protected void onProgressUpdate(final Movie... values) {
			super.onProgressUpdate(values);

			progressCallBack.showSpinner();
		}

		@Override
		protected Integer doInBackground(final ListWrapper... params) {
			List<Movie> movies = params[0].movies;

			Log.d(TAG, "do in background");

			for (Movie movie : movies) {
				setPosterToMovie(movie);
				publishProgress(movie);
			}

			return 0;
		}

		@Override
		protected void onPostExecute(final Integer result) {
			super.onPostExecute(result);

			if (moviePosterCallBack != null) {
				moviePosterCallBack.posterLoaded();
			}

			progressCallBack.hideSpinner();
		}
	}

	private class GetMovieDetailsTask extends
			AsyncTask<Movie, Void, MovieDetailsData> {

		private MovieDetailsCallBack movieDetailsCallBack;

		public GetMovieDetailsTask(
				final MovieDetailsCallBack movieDetailsCallBack) {
			this.movieDetailsCallBack = movieDetailsCallBack;
		}

		@Override
		protected void onPreExecute() {
			super.onPreExecute();

			progressCallBack.showSpinner();
		}

		@Override
		protected void onCancelled() {
			super.onCancelled();

			progressCallBack.hideSpinner();
		}

		@Override
		protected MovieDetailsData doInBackground(final Movie... params) {
			MovieDetailsData data = new MovieDetailsData();

			try {
				data.setMovieDetails(getMovieDetails(params[0]));
			} catch (Exception e) {
				String err = (e.getMessage() == null) ? "SD Card failed" : e
						.getMessage();
				Log.e(TAG, err, e);

				data.setMovieDetails(null);
				data.setException(e);
			}
			return data;
		}

		@Override
		protected void onPostExecute(final MovieDetailsData data) {
			super.onPostExecute(data);
			if (data.getException() == null) {
				movieDetailsCallBack.postExecute(data);
			} else {
				progressCallBack.hideSpinner();
				progressCallBack.showAlertDialog("Err:"
						+ data.getException().getMessage());
			}
		}
	}

	private class GetPosterTaskTask extends AsyncTask<Movie, Void, Movie> {

		private final MovieDetailsCallBack movieDetailsCallback;

		public GetPosterTaskTask(final MovieDetailsCallBack movieListCallBack) {
			movieDetailsCallback = movieListCallBack;
		}

		@Override
		protected Movie doInBackground(final Movie... params) {
			Movie movie = params[0];

			setPosterToMovie(movie);

			return movie;
		}

		@Override
		protected void onPostExecute(final Movie result) {
			super.onPostExecute(result);

			movieDetailsCallback.loadPoster(result);
		}
	}

	private class GetMovieDetailsImagesTask extends
			AsyncTask<MovieDetails, Pair, Integer> {

		private MovieDetailsCallBack movieDetailsCallBack;

		public GetMovieDetailsImagesTask(
				final MovieDetailsCallBack movieDetailsCallBack) {
			this.movieDetailsCallBack = movieDetailsCallBack;
		}

		@Override
		protected void onPreExecute() {
			super.onPreExecute();

			progressCallBack.showSpinner();
		}

		@Override
		protected void onProgressUpdate(final Pair... values) {
			super.onProgressUpdate(values);

			Pair pair = values[0];

			movieDetailsCallBack.loadImage(pair.image, pair.index);
		}

		@Override
		protected Integer doInBackground(final MovieDetails... params) {
			MovieDetails details = params[0];

			List<String> localUrls = new ArrayList<String>();

			for (int i = 0; i < details.getThumbHttpUrls().size(); i++) {

				Pair pair = getMovieDetailsImage(details, i);
				localUrls.add(pair.image.getFileName());
				publishProgress(pair);
			}

			details.setThumbLocalUrls(localUrls);
			return 0;
		}

		@Override
		protected void onPostExecute(final Integer result) {
			super.onPostExecute(result);

			progressCallBack.hideSpinner();
		}
	}

	public void setProgressCallBack(final ProgressCallBack progressCallBack) {
		this.progressCallBack = progressCallBack;
	}
}
